package com.gee.spring.boot.starter.opengemini.core.util;

import java.lang.reflect.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @author gepengjun
 * @since 2024/4/28 10:26
 */
public class ReflectionUtil {

    //className fieldName Field
    private static final ConcurrentMap<String, ConcurrentMap<String, Field>> CLASS_FIELD_CACHE
            = new ConcurrentHashMap<>();

    private static ClassLoader systemClassLoader;

    static {
        try {
            systemClassLoader = ClassLoader.getSystemClassLoader();
        } catch (SecurityException ignored) {
            // AccessControlException on Google App Engine
        }
    }

    public static <T> T newInstance(Class<T> clazz) {
        try {
            Constructor<T> constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(true);
            return constructor.newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException(String.format("实例化对象时出现错误,请尝试给 %s 添加无参的构造方法", clazz.getName()), e);
        }
    }

    public static <T> T newInstance(String clazzName) {
        return (T) newInstance(toClassConfident(clazzName));
    }

    public static Class<?> toClassConfident(String name) {
        return toClassConfident(name, null);
    }

    public static Class<?> toClassConfident(String name, ClassLoader classLoader) {
        try {
            return loadClass(name, getClassLoaders(classLoader));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("找不到指定的class！请仅在明确确定会有 class 的时候，调用该方法", e);
        }
    }

    private static Class<?> loadClass(String className, ClassLoader[] classLoaders) throws ClassNotFoundException {
        for (ClassLoader classLoader : classLoaders) {
            if (classLoader != null) {
                try {
                    return Class.forName(className, true, classLoader);
                } catch (ClassNotFoundException e) {
                    // ignore
                }
            }
        }
        throw new ClassNotFoundException("Cannot find class: " + className);
    }

    private static ClassLoader[] getClassLoaders(ClassLoader classLoader) {
        return new ClassLoader[]{
                classLoader,
                Thread.currentThread().getContextClassLoader(),
                ReflectionUtil.class.getClassLoader(),
                systemClassLoader};
    }

    public static Field getField(Class<?> clazz, String fieldName){
        ConcurrentMap<String, Field> fieldMap = CLASS_FIELD_CACHE.computeIfAbsent(clazz.getName(), className -> new ConcurrentHashMap<>());
        return fieldMap.computeIfAbsent(fieldName, fN -> {
            Class<?> c = clazz;
            while (c != null) {
                for (Field field : c.getDeclaredFields()) {
                    if (fN.equals(field.getName())) {
                        field.setAccessible(true);
                        return field;
                    }
                }
                c = c.getSuperclass();
            }
            throw ExceptionUtil.re("can not find field [%s] in class [%s]", fN, clazz.getName());
        });
    }

    public static Object getFieldValue(Object obj, Field field){
        try {
            return field.get(obj);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public static void setFieldValue(Object obj, Field field, Object value){
        try {
            field.set(obj, value);
        } catch (IllegalAccessException e) {
            throw ExceptionUtil.re("can not set field  [%s] value [%s] in object [%s]", field.getName(), value, obj);
        }
    }

    public static Type getSaveType(Type type){
        return getTypeArguments(type)[1];
    }

    public static Type[] getTypeArguments(Type type) {
        if (null == type) {
            return null;
        }
        final ParameterizedType parameterizedType = toParameterizedType(type);
        return (null == parameterizedType) ? null : parameterizedType.getActualTypeArguments();
    }

    public static ParameterizedType toParameterizedType(Type type) {
        ParameterizedType result = null;
        if (type instanceof ParameterizedType) {
            result = (ParameterizedType) type;
        } else if (type instanceof Class) {
            final Class<?> clazz = (Class<?>) type;
            Type genericSuper = clazz.getGenericSuperclass();
            if (null == genericSuper || Object.class.equals(genericSuper)) {
                // 如果类没有父类，而是实现一些定义好的泛型接口，则取接口的Type
                final Type[] genericInterfaces = clazz.getGenericInterfaces();
                if (genericInterfaces.length > 0) {
                    // 默认取第一个实现接口的泛型Type
                    genericSuper = genericInterfaces[0];
                }
            }
            result = toParameterizedType(genericSuper);
        }
        return result;
    }

    public static <T extends AccessibleObject> T setAccessible(T object) {
        return AccessController.doPrivileged(new SetAccessibleAction<>(object));
    }

    static class SetAccessibleAction<T extends AccessibleObject> implements PrivilegedAction<T> {
        private final T obj;

        public SetAccessibleAction(T obj) {
            this.obj = obj;
        }

        @Override
        public T run() {
            obj.setAccessible(true);
            return obj;
        }

    }
}
